package com.derbysoft.synxisadapter.dswitch2.service.impl;

import com.derbysoft.remote.dswitch.core.GenericRQ;
import com.derbysoft.remote.dswitch.core.GenericRS;
import com.derbysoft.remote.dswitch.core.SuccessDTO;
import com.derbysoft.remote.dswitch.hotel.dto.HotelAvailRQ;
import com.derbysoft.remote.dswitch.hotel.dto.HotelAvailRS;
import com.derbysoft.remote.dswitch.hotel.dto.HotelReservationRQ;
import com.derbysoft.remote.dswitch.hotel.dto.HotelReservationRS;
import com.derbysoft.synxisadapter.commons.dto.synxis.HotelReservation;
import com.derbysoft.synxisadapter.commons.dto.synxis.OTAHotelResRQ;
import com.derbysoft.synxisadapter.commons.dto.synxis.OTAHotelResRS;
import com.derbysoft.synxisadapter.commons.dto.synxis.RoomStay;
import com.derbysoft.synxisadapter.commons.model.Channel;
import com.derbysoft.synxisadapter.commons.model.Reservation;
import com.derbysoft.synxisadapter.commons.repository.HotelRepository;
import com.derbysoft.synxisadapter.commons.repository.ReservationRepository;
import com.derbysoft.synxisadapter.dswitch2.service.HotelProviderService;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

public class HotelReservationServiceImpl extends AbstractReservationService {

    @Override
    public GenericRS<HotelReservationRS> invoke(GenericRQ<HotelReservationRQ> rq) {
        Channel channel = findChannel(rq.getHeader().getSource().getUniqueID());

        String ersp = saveReservation(rq.getPayload());

        GenericRS<HotelAvailRS> availRS = getAvailRS(rq);

        if (availRS.hasErrors()) {
            updateStatus(ersp, Reservation.Status.BOOK_FAILED);
            return availErrorRS(rq, availRS);
        }
        if (CollectionUtils.isEmpty(availRS.getPayload().getHotelAvailRoomStay().getRoomStays())
                || findRoomRate(availRS.getPayload(), rq.getPayload().getHotelReservation()) == null) {
            updateStatus(ersp, Reservation.Status.BOOK_FAILED);
            return noAllotmentRS(rq);
        }

        List<OTAHotelResRQ> requestList = requestTranslator.translate(rq, null);

        List<OTAHotelResRS> responseList = new ArrayList<OTAHotelResRS>();

        for (OTAHotelResRQ otaHotelResRQ : requestList) {
            OTAHotelResRS otaHotelResRS = (OTAHotelResRS) providerWebService.invoke(otaHotelResRQ, channel);
            if (hasError(otaHotelResRS)) {
                updateStatus(ersp, Reservation.Status.BOOK_FAILED);
                throw new IllegalStateException(otaHotelResRS.getErrors().getError().get(0).getShortText());
            }
            if (otaHotelResRS.getHotelReservations() == null ||
                    CollectionUtils.isEmpty(otaHotelResRS.getHotelReservations().getHotelReservation())
                    || otaHotelResRS.getHotelReservations().getHotelReservation().get(0).getRoomStays() == null
                    || CollectionUtils.isEmpty(otaHotelResRS.getHotelReservations().getHotelReservation().get(0).getRoomStays().getRoomStay())) {
                updateStatus(ersp, Reservation.Status.BOOK_FAILED);
                return noAllotmentRS(rq);
            }
            responseList.add(otaHotelResRS);

            addReservationItem(ersp, otaHotelResRS.getHotelReservations().getHotelReservation().get(0));
        }

        GenericRS<HotelReservationRS> rs = responseTranslator.translate(responseList, rq);

        rs.getPayload().setCancelPolicy(findCancelPolicy(availRS.getPayload(), rq.getPayload().getHotelReservation()));
        rs.setSuccess(new SuccessDTO());
        rs.setHeader(createHeader(rq));

        updateStatus(ersp, Reservation.Status.CONFIRMED);

        return rs;
    }

    private void addReservationItem(String ersp, HotelReservation hotelReservation) {
        RoomStay roomStay = hotelReservation.getRoomStays().getRoomStay().get(0);
        String roomTypeCode = roomStay.getRoomTypes().getRoomType().get(0).getRoomTypeCode();
        String ratePlaneCode = roomStay.getRatePlans().getRatePlan().get(0).getRatePlanCode();
        String confirmNumber = hotelReservation.getUniqueID().getID();
        reservationRepository.addReservationItem(ersp, roomTypeCode, ratePlaneCode, confirmNumber);
    }

    public void setReservationRepository(ReservationRepository reservationRepository) {
        this.reservationRepository = reservationRepository;
    }

    public void setHotelRepository(HotelRepository hotelRepository) {
        this.hotelRepository = hotelRepository;
    }

    public void setHotelAvailService(HotelProviderService<GenericRQ<HotelAvailRQ>, GenericRS<HotelAvailRS>> hotelAvailService) {
        this.hotelAvailService = hotelAvailService;
    }
}